package com.gframework.paramparse.points;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;

/**
 * 点段式参数解析器工具类，本类可以帮助你在某些大量数据，如集合，多个属性的对象进行参数解析时更加方便。
 * <p>
 * 但是所有对象要处理的属性必须提供public的setter和getter方法，同时也支持数组，集合和map。
 * </p>
 * <p>
 * 请勿直接实例化本类对象，当GenericPointSegParamParseContext被spring注入后，此类会自动成为一个bean
 * </p>
 *
 * @since 1.0.0
 * @author Ghwolf
 * 
 * @see GenericPointSegParamParseContext
 */
public class PointSegParamUtils {
	private static final Logger logger = LoggerFactory.getLogger(PointSegParamUtils.class);

	/**
	 * 点断式参数解析器
	 */
	private static PointSegParamParseContext paramParse;

	private PointSegParamUtils() {
	}
	
	/**
	 * 初始化
	 */
	static void init(PointSegParamParseContext paramParse){
		if (paramParse != null) {
			PointSegParamUtils.paramParse = paramParse;
		}
	}
	
	/**
	 * 提供一个获取参数和设置参数的方法，获取参数并进行参数解析处理然后设置到setter方法中.
	 * 如果参数为null则不做任何事情。
	 * @param getter getter方法
	 * @param setter setter方法
	 */
	public static void parse(Supplier<? super String> getter,Consumer<? super String> setter) {
		if (getter == null || setter == null) return;
		
		Object o = getter.get();
		if (o != null) {
			setter.accept(parse(o.toString()));
		}
	}
	
	/**
	 * 对javabean中的一个属性进行参数解析操作，但必须提供有setter和getter方法，否则不做任何事情。
	 * 如果参数为null，则不做任何事情。
	 * @param property 属性对象，通过Introspector和BeanInfo取得的对象
	 * @param obj 要处理的对象
	 * @see Introspector
	 */
	public static void parse(PropertyDescriptor property,Object obj) {
		if (obj == null || property == null || property.getWriteMethod() == null || property.getReadMethod() == null){
			return;
		}
		try {
			Object o = invokeMethod(property.getReadMethod(),obj);
			if (o != null) {
				invokeMethod(property.getWriteMethod(),obj,parse(o.toString()));
			}
		} catch(Exception e) {
			logger.error("解析对象属性参数时，调用setter和getter方法异常：{}。",property,e);
		}
	}

	/**
	 * 解析一个map中指定key值得参数，如果参数不存在，或者map、keys为null，则不做任何事情.
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param map 要进行解析得map对象
	 * @param keys 要进行处理得参数key
	 */
	@SafeVarargs
	public static <T> void parse(Map<T, ? super String> map, T... keys) {
		if (map == null || map.isEmpty() || keys == null || keys.length == 0) {
			return;
		}
		for (T key : keys) {
			map.computeIfPresent(key, (k, v) -> PointSegParamUtils.parse(v.toString()));
		}
	}

	/**
	 * 解析一个list中所有map中指定key值的参数，如果参数不存在，或者list、keys为null，则忽略.
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param list 要进行解析得list对象，将会对其中每一个map进行解析
	 * @param keys 要进行处理得参数key
	 */
	@SafeVarargs
	public static <T> void parse(List<Map<T, ? super String>> list, final T... keys) {
		if (list == null || list.isEmpty() || keys == null || keys.length == 0) {
			return;
		}
		list.forEach(m -> PointSegParamUtils.parse(m, keys));
	}
	
	
	/**
	 * 递归解析一个obj中指定属性值的参数，如果属性不存在，或者参数为null，则忽略.
	 * <pre>
	 * 此方法是一个深度递归解析的操作，指定属性可以是字符串类型及其父类，以及数组，集合，map和其他对象，
	 * 如果遇到其他对象，则会继续解析此对象中的属性。
	 * 如果遇到数组或集合，则会遍历其中的每一个对象继续解析，如果是字符串不会被直接解析，因为只能够对属性进行解析。
	 * 如果遇到map，则会通过指定的属性名称集合去查找其key值，如果存在，则处理。
	 * <strong>注意：请避免出现循环引用问题，否则将无法处理。</strong>
	 * </pre>
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param obj 要进行解析得obj对象
	 * @param fields 属性名称集合
	 */
	public static void parseDeep(Object obj, final String ... fields) {
		if (obj == null || fields == null || fields.length == 0) {
			return;
		}
		dispatcherByType(obj.getClass(),obj,fields);
	}
	
	/**
	 * 递归解析一个map中指定属性值的参数，如果属性不存在，或者参数为null，则忽略.
	 * <pre>
	 * 此方法是一个深度递归解析的操作，指定属性可以是字符串类型及其父类，以及数组，集合，map和其他对象，
	 * 如果遇到其他对象，则会继续解析此对象中的属性。
	 * 如果遇到数组或集合，则会遍历其中的每一个对象继续解析，如果是字符串不会被直接解析，因为只能够对属性进行解析。
	 * 如果遇到map，则会通过指定的属性名称集合去查找其key值，如果存在，则处理。
	 * <strong>注意：请避免出现循环引用问题，否则将无法处理。</strong>
	 * </pre>
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param map 要进行解析得map对象，将会对其中每一个对象进行解析，对象类型可以不一样，可以是bean，数组，集合和map
	 * @param fields 属性名称集合
	 */
	public static void parseDeep(Map<?,?> map, final String ... fields) {
		if (map == null || map.isEmpty() || fields == null || fields.length == 0) {
			return;
		}
		parseDeep0(map,fields);
	}

	/**
	 * 递归解析一个集合中所有对象中指定属性值的参数，如果属性不存在，或者参数为null，则忽略.
	 * <pre>
	 * 此方法是一个深度递归解析的操作，指定属性可以是字符串类型及其父类，以及数组，集合，map和其他对象，
	 * 如果遇到其他对象，则会继续解析此对象中的属性。
	 * 如果遇到数组或集合，则会遍历其中的每一个对象继续解析，如果是字符串不会被直接解析，因为只能够对属性进行解析。
	 * 如果遇到map，则会通过指定的属性名称集合去查找其key值，如果存在，则处理。
	 * <strong>注意：请避免出现循环引用问题，否则将无法处理。</strong>
	 * </pre>
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param list 要进行解析得集合对象，将会对其中每一个对象进行解析，对象类型可以不一样，可以是bean，数组，集合和map
	 * @param fields 属性名称集合
	 */
	public static void parseDeep(Collection<?> list, final String ... fields) {
		if (list == null || list.isEmpty() || fields == null || fields.length == 0) {
			return;
		}
		parseDeep0(list,fields);
	}
	/**
	 * 递归解析一个数组中所有对象中指定属性值的参数，如果属性不存在，或者参数为null，则忽略.
	 * <pre>
	 * 此方法是一个深度递归解析的操作，指定属性可以是字符串类型及其父类，以及数组，集合，map和其他对象，
	 * 如果遇到其他对象，则会继续解析此对象中的属性。
	 * 如果遇到数组或集合，则会遍历其中的每一个对象继续解析，如果是字符串不会被直接解析，因为只能够对属性进行解析。
	 * 如果遇到map，则会通过指定的属性名称集合去查找其key值，如果存在，则处理。
	 * <strong>注意：请避免出现循环引用问题，否则将无法处理。</strong>
	 * </pre>
	 * <p>
	 * 此方法自己不会产生异常，但不能保证参数解析器解析过程中不会出现异常。
	 * </p>
	 * 
	 * @param array 要进行解析得数组对象，将会对其中每一个对象进行解析，对象类型可以不一样，可以是bean，数组，集合和map
	 * @param fields 属性名称集合
	 */
	public static void parseDeep(Object[] array, final String ... fields) {
		if (array == null || array.length == 0 || fields == null || fields.length == 0) {
			return;
		}
		parseDeep0(array,fields);
	}

	
	/**
	 * 解析数组中所有对象，不做参数检查
	 */
	private static void parseDeep0(Object[] array, final String ... fields) {
		for (Object obj : array) {
			if (obj != null) {
				dispatcherByType(obj.getClass(),obj,fields);
			}
		}
	}
	/**
	 * 解析map中所有对象，不做参数检查
	 */
	private static void parseDeep0(Map<?,?> map, final String ... fields) {
		for (String field : fields) {
			
			Object obj = map.get(field);
			if (obj != null) {
				dispatcherByType(obj.getClass(),obj,fields);
			}
		}
	}
	/**
	 * 解析集合中所有对象，不做参数检查
	 */
	private static void parseDeep0(Collection<?> list, final String ... fields) {
		list.forEach(obj->{
			if (obj != null) {
				dispatcherByType(obj.getClass(),obj,fields);
			}
		});
	}
	/**
	 * 解析一个<strong>不是集合，数组，map</strong>的对象，获取属性并进行处理.
	 * <p>
	 * 不做参数检查
	 * </p>
	 * @param obj 要处理的object对象
	 * @param fields 要处理的属性
	 */
	private static void parseDeep0(Object obj, final String ... fields) {
		BeanInfo info;
		try {
			info = Introspector.getBeanInfo(obj.getClass());
		} catch (IntrospectionException e) {
			// ignore
			throw new RuntimeException(e);
		}
		for (String field : fields) {
			if (fields == null) continue ;
			
			PropertyDescriptor pro = findProperty(info.getPropertyDescriptors(),field);
			if (pro == null) continue ;
			
			Class<?> type = pro.getPropertyType();
			if (type == null) continue ;
			
			try {
				Object fieldObj = invokeMethod(pro.getReadMethod(),obj);
				if (fieldObj == null) continue ;
				
				if (fieldObj.getClass().isAssignableFrom(String.class)) {
					invokeMethod(pro.getWriteMethod(),obj,parse(fieldObj.toString()));
				} else if (type.isArray()) {
					parseDeep0((Object[])fieldObj,fields);
				} else if (Collection.class.isAssignableFrom(type)) {
					parseDeep0((Collection<?>)fieldObj,fields);
				} else if (Map.class.isAssignableFrom(type)) {
					parseDeep0((Map<?,?>)fieldObj,fields);
				} else {
					parseDeep0(fieldObj,fields);
				}
			} catch(Exception e) {
				logger.error("解析对象属性参数时，调用setter和getter方法异常：{}。",pro,e);
			}
		}
	}
	
	/**
	 * 根据类型自动选择使用哪种方式解析和处理，如数组，集合，map和对象的处理方法是完全不一样的。
	 * <p>
	 * 不做参数检查
	 * </p>
	 * @param cls 要判断的类型
	 * @param obj 待处理的对象
	 * @param fields 要查询的列
	 */
	private static void dispatcherByType(Class<?> cls,Object obj,String ... fields) {
		if (cls.isArray()) {
			parseDeep0((Object[])obj,fields);
		} else if (Map.class.isAssignableFrom(cls)) {
			parseDeep0((Map<?,?>)obj,fields);
		} else if (Collection.class.isAssignableFrom(cls)) {
			parseDeep0((Collection<?>)obj,fields);
		} else {
			parseDeep0(obj,fields);
		}
	}
	
	/**
	 * 从PropertyDescriptor数组中寻找具有指定属性名称且拥有getter的属性
	 * @param pros 属性集合
	 * @param name 属性名称
	 * @return 存在返回PropertyDescriptor，否则返回null
	 */
	@Nullable
	private static PropertyDescriptor findProperty(PropertyDescriptor[] pros,String name) {
		for (int x = 0 ; x < pros.length ; x ++) {
			PropertyDescriptor p = pros[x];
			if (name.equals(p.getName()) && p.getReadMethod() != null) {
				return p ;
			}
		}
		return null ;
	}

	/**
	 * 执行此方法可以进行参数解析操作，根据参数解析器接口规范，返回结果一定不是null
	 */
	private static String parse(String str) {
		if (paramParse == null) {
			throw new IllegalStateException(
					PointSegParamUtils.class.getName() + " 类还未初始化 或 未启用 GenericPointSegParamParseContext 参数解析器！");
		}
		return paramParse.parse(str);
	}

	/**
	 * 执行一个方法，如果是封装的，则解除封装。
	 * @param method 方法对象
	 * @param obj 执行对象
	 * @param args 参数
	 * @return 如果有返回值则返回，否则返回null
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 */
	private static Object invokeMethod(Method method, Object obj, Object... args)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		if (!Modifier.isPublic(method.getDeclaringClass().getModifiers()) && !method.isAccessible()) {
			method.setAccessible(true);
		}
		return method.invoke(obj, args);
	}
}
